<?php

/**
 * PHP SDK FOR WWW.YIBAN.CN[OAuth2.0]
 * 
 * 易班 OAuth2.0 授权类
 * 
 * 详细的授权机制，以及Api描述，请大家参考开放平台文档
 * 
 * @author ivanzz ivanzzlxp@gmail.com
 * @version 1.0 2012/10/5 10:20
 * @package	Open_Platform_SDK
 * @link http://open.yiban.cn/wiki/
 * 
 */
class YibanOAuth {
	
	/**
	 * Used to set appkey
	 * 
	 * @ignore
	 */
	public $client_id;
	
	/**
	 * Used to set AppSecret
	 * 
	 * @ignore
	 */
	public $client_secret;
	
	/**
	 * Used to set access_token
	 * 
	 * @ignore
	 */
	public $access_token;
	
	/**
	 * Used to set refresh_token
	 *
	 * @ignore
	 */
	public $refresh_token;
	
	/**
	 * Get Api URL
	 * 
	 * @ignore
	 */
	public $host = 'http://api.yiban.cn/';
	
	/**
	 * Get return format
	 *
	 * @ignore
	 */
	public $format = 'json';
	
	/**
	 * @ignore
	 */
	public static $boundary = '';
	
	/**
	 * @ignore
	 */
	public $http_info;
	
	/**
	 * Prepare for next version
	 * 
	 * @ignore
	 */
	public $useragent = 'Yi OAuth2 v0.1';
	
	/**
	 * Set true to help you debug
	 * 
	 * @ignore
	 */
	public $debug = false;
	
	/**
	 * Set the timeout
	 * 
	 * @ignore
	 */
	public $timeout = 30;
	
	/**
	 * Set the connection timeout
	 * 
	 * @ignore
	 */
	public $connecttimeout = 30;
	
	/**
	 * 
	 * @ignore
	 */
	public $ssl_verifypeer = false;
	
	/**
	 * Get return http code
	 * 
	 * @ignore
	 */
	public $http_code;
	
	/**
	 * 
	 * @ignore
	 */
	public $url;
	
	/**
	 * 
	 * @ignore
	 */
	public $http_header;
	
	/**
	 * Use array to dev
	 * 
	 * @ignore
	 */
	public $decode_json = true;
	
	/**
	 * Initialize Authentication Configuration
	 * 
	 * @param string $client_id
	 * @param string $client_secret
	 * @param string $access_token
	 * @param string $refresh_token
	 */
	public function __construct( $client_id, $client_secret, $access_token = null, $refresh_token = null ) {
		$this->client_id = $client_id;
		$this->client_secret = $client_secret;
		$this->access_token = $access_token;
		$this->refresh_token = $refresh_token;
	}
	
	/**
	 * Combination A Full Authorize URL
	 * 
	 * @param string $url 授权后的回调地址,站外应用需与回调地址一致,站内应用需要填写canvas page的地址
	 * @param string $response_type 支持的值包括 code 和token 默认值为code
	 * @param string $state 用于保持请求和回调的状态。在回调时,会在Query Parameter中回传该参数
	 * @param string $display 暂时默认选择
	 * 
	 * @return array
	 */
	public function getFullAuthorizeUrl( $url, $response_type = 'code', $state = null, $display = null ) {
		$params = array();
		$params['client_id'] = $this->client_id;
		$params['redirect_uri'] = $url;
		$params['response_type'] = $response_type;
		$params['state'] = $state;
		$params['display'] = $display;
		return $this->getAuthorizeUrl() . '?' . http_build_query( $params );
	}
	
	/**
	 * Get Access_token
	 * 
	 * @param string $type Request Type:code, password, token
	 * @param array $keys
	 * @throws OAuthException
	 * @return array
	 */
	public function getAccessToken( $type = 'code', $keys ) {
		$params = array();
		$params['client_id'] = $this->client_id;
		$params['client_secret'] = $this->client_secret;
		if ( $type === 'token' ) {
			$params['grant_type'] = 'refresh_token';
			$params['refresh_token'] = $keys['refresh_token'];
		} elseif ( $type === 'code' ) {
			$params['grant_type'] = 'authorization_code';
			$params['code'] = $keys['code'];
			$params['redirect_uri'] = $keys['redirect_uri'];
		} elseif ( $type === 'password' ) {
			$params['grant_type'] = 'password';
			$params['username'] = $keys['username'];
			$params['password'] = $keys['password'];
		} else {
			throw new OAuthException( 'wrong auth type' );
		}
		
		$response = $this->oAuthRequest($this->getTokenUrl(), 'POST', $params);
		$token = json_decode( $response ,true );
		if ( is_array( $token ) && !isset( $token['error'] ) ) {
			$this->access_token = $token['access_token'];
			$this->refresh_token = $token['refresh_token'];
		} else {
			throw new OAuthException("get access token failed." . $token['error']);
		}
		return $token;
		
	}
	
	/**
	 * Format and sign an OAuth / API request
	 * 
	 * @param string $url
	 * @param string $method
	 * @param array $parameters
	 * @param boolean $multi
	 */
	public function oAuthRequest( $url, $method, $parameters, $multi = false ) {
		if ( strrpos( $url, 'http://' ) !== 0 && strrpos( $url, 'https://') !== 0 ) {
			$url = "{$this->host}{$url}.{$this->format}";
		}
		$parameters['access_token'] = $this->access_token;
		switch ( $method ) {
			case 'GET':
				$url = $url . '?' . http_build_query($parameters);
				return $this->http($url, 'GET');
			default:
				$headers = array();
				if (!$multi && ( is_array( $parameters ) ) || is_object( $parameters ) ) {
					$body = http_build_query( $parameters );
				} else {
					$body = self::build_http_query_multi( $parameters );
					$headers[] = "Content-Type: multipart/form-data; boundary=" . self::$boundary;
				}
				return $this->http( $url, $method, $body, $headers );
		}
	}
	
	/**
	 * Make an HTTP Request
	 * 
	 * @param string $url
	 * @param string $method
	 * @param array $postfields
	 * @param array $headers  currently don't support
	 * @return mixed 
	 */
	public function http( $url, $method, $postfields = null, $headers = array() ) {
		
		$this->http_info = array();
		$ch = curl_init();
		
		curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
		curl_setopt($ch, CURLOPT_USERAGENT, $this->useragent);
		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout);
		curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
		curl_setopt($ch, CURLOPT_ENCODING, "");
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer);
		curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, 'getHeader'));
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		
		switch ( $method ) {
			case 'POST':
				curl_setopt($ch, CURLOPT_POST, TRUE);
				if (!empty($postfields)) {
					curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields);
					$this->postdata = $postfields;
				}
				break;
			case 'DELETE':
				curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
				if (!empty($postfields)) {
					$url = "{$url}?{$postfields}";
				}
		}
		if ( isset($this->access_token) && $this->access_token )
			$headers[] = "Authorization: OAuth2 ".$this->access_token;
		$headers[] = "API-RemoteIP: " . $_SERVER['REMOTE_ADDR'];
		
		curl_setopt($ch, CURLOPT_URL, $url );
		curl_setopt($ch, CURLOPT_HTTPHEADER, $headers );
		curl_setopt($ch, CURLINFO_HEADER_OUT, TRUE );
		
		$response = curl_exec($ch);
		
		$this->http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE);
		$this->http_info = array_merge($this->http_info, curl_getinfo($ch));
		$this->url = $url;
		
		if ( $this->debug ) {
			echo "=====post data=====\r\n";
			var_dump($postfields);
			
			echo "=====info=====\r\n";
			print_r( curl_getinfo( $ch ));
			
			echo "=====response=====\r\n";
			print_r( $response );
		}
		
		curl_close($ch);
		return $response;
	}
	 
	/**
	 * Get the header info to store.
	 * 
	 * @param unknown_type $ch
	 * @param unknown_type $header
	 * @return int
	 */
	public function getHeader( $ch, $header ) {
		$i = strpos( $header, ':');
		if ( !empty($i) ) {
			$key = str_replace('-', '_', strtolower( substr($header, 0, $i)));
			$value = trim(substr($header, $i + 2));
			$this->http_header[$key] = $value;
		}
		return strlen( $header );
	}
	
	/**
	 * @ignore
	 */
	public static function build_http_query_multi( $params ){
		if ( !$params ) return '';
		uksort( $params, 'strcmp' );
		$pairs = array();
		
		self::$boundary = $boundary = uniqid('-----------------');
		$MPboundary = '--'.$boundary;
		$endMPboundary = $MPboundary. '--';
		$multipartbody = '';
		
		foreach ( $params as $parameter => $value ) {
			if ( in_array( $parameter, array('pic', 'image')) && $value{0} == '@' ) {
				$url = ltrim( $value, '@' );
				$content = file_get_contents( $url );
				$array = explode( '?', basename( $url ) );
				$filename = $array[0];
				
				$multipartbody .= $MPboundary . "\r\n";
				$multipartbody .= 'Content-Disposition: form-data; name="' . $parameter . '"; filename="' . $filename . '"'. "\r\n";
				$multipartbody .= "Content-Type: image/unknown\r\n\r\n";
				$multipartbody .= $content. "\r\n";
				
			} else {
				$multipartbody .= $MPboundary . "\r\n";
				$multipartbody .= 'content-disposition: form-data; name="' . $parameter . "\"\r\n\r\n";
				$multipartbody .= $value."\r\n";
			}
			
			$multipartbody .= $endMPboundary;
			return $multipartbody;
			
		}
	}
	
	/**
	 * 解析 signed_request
	 *
	 * @param string $signed_request 应用框架在加载iframe时会通过向Canvas URL post的参数signed_request
	 *
	 * @return array
	 */
	function parseSignedRequest($signed_request) {
		list($encoded_sig, $payload) = explode('.', $signed_request, 2);
		$sig = base64_decode($encoded_sig);
		$data = json_decode(base64_decode($payload), true);
		if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') return '-1';
		$expected_sig = hash_hmac('sha256', $payload, $this->client_secret);
		return ($sig !== $expected_sig)? '-2':$data;
	}
	
	/**
	 * Cast coding To UTF8
	 * 
	 * @param $array
	 */
	public function encodeUTF8(&$array){
	
		if(!is_array($array))
			return iconv('gbk', 'utf8', $array);
		foreach($array as $key=>$value){
			if(!is_array($value))
				$array[$key]=mb_convert_encoding($value,"UTF-8","GBK"); //由gbk转换到utf8
			else
				self::encodeUTF8($array[$key]);
		}
		return $array;
	}
	
	/**
	 * Cast coding to GBK
	 * 
	 * @param $array
	 */
	protected function encodeGBK(&$array){
		if(!is_array($array))
			return iconv('utf8', 'gbk', $array);
		foreach($array as $key=>$value){
			if(!is_array($value))
				$array[$key]=mb_convert_encoding($value,"GBK","UTF-8"); //由gbk转换到utf8
			else
				self::encodeGBK($array[$key]);
		}
		return $array;
	}
	
	/**
	 * GET wrappwer for oAuthRequest.
	 * 
	 * @param string $url
	 * @param array $parameters
	 */
	public function get( $url, $parameters = array() ) {
		$response = $this->oAuthRequest($url, 'GET', $parameters);
		if ( $this->format === 'json' && $this->decode_json) {
			return json_decode( $response, true );
		}
		return $response;
	}
	
	/**
	 * POST wreapper for oAuthRequest.
	 * 
	 * @param string $url
	 * @param array $parameters
	 * @param boolean $multi
	 */
	public function post( $url, $parameters = array(), $multi = false ) {
		$response = $this->oAuthRequest($url, 'POST', $parameters, $multi );
		if ( $this->format === 'json' && $this->decode_json ) {
			return json_decode( $response ,true );
		}
		return $response;
	}
	
	/**
	 * @ignore
	 */
	public function getTokenUrl(){
		return 'https://graph.yiban.cn/oauth/token.php';
	}
	
	/**
	 * @ignore
	 */
	public function getAuthorizeUrl(){
		return 'https://graph.yiban.cn/oauth/authorize.php';
	}
	 
}

/**
 * 易班API资源操作类
 * 
 * 实际开发中，请在调用前，先引入Yiban.class.php文件
 * 
 * @author ivanzz ivanzzlxp@gmail.com
 * @version 1.0 2012/10/18 16:12
 * @link http://open.yiban.cn/wiki/
 *
 */
class YibanApi {
	
	public $oauth;
	
	public function __construct( $akey, $skey, $access_token, $refresh_token = null) {
		$this->oauth = new YibanOAuth($akey, $skey, $access_token, $refresh_token);
	}
	
	public function get_show( $uid ) {
		$params = array();
		$params['uid'] = $uid;
		return $this->oauth->get( 'users/get_show', $params );
	}
	
	//..持续更新..
	
	
}

/**
 * Exception
 * 
 * @author ivanzz
 * @version 1.0
 *
 */
class OAuthException extends Exception {
	//...
}




?>